import selectors
import socket

from FPDLCore import Configs
from FPDLCore import MessageDefs


class BaseClientNet:
    def __init__(self, remoteaddr, sel):
        self.remoteaddr = remoteaddr
        self.sel = sel
        self._base_client_net_data_to_send = None

    def connect(self):
        def onconnected(conn, mask):
            error = conn.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
            if error == 0:
                self.waitread(conn)
                self.onconnectionmade(conn)
            else:
                self.onconnectionfail(conn)
                self.close(conn)

        def read(conn, mask):
            try:
                data = conn.recv(Configs.RECIVE_BUFFER_SIZE)
            except ConnectionError as e:
                print("on event read", conn, e)
                data = None
            if data:
                self.onread(conn, data)
            else:
                self.close(conn)

        def send(conn, mask):
            try:
                if not self._checkanddosendjob(conn):
                    self.onsend(conn)
            except ConnectionError as e:
                print("on event send", conn, e)
                self.close(conn)

        self._onconnected = onconnected
        self._read = read
        self._send = send
        try:
            self.sock = socket.socket()
            self.sock.connect(self.remoteaddr)
            self.sock.setblocking(False)
            self.sel.register(self.sock, selectors.EVENT_WRITE, self._onconnected)
        except Exception as e:
            print(e)
            return False

        return True

    def send(self, obj, onsendend=None):
        self._base_client_net_data_to_send = MessageDefs.serialize(obj)
        self._base_client_net_onsendend = onsendend
        self.waitsend(self.sock)

    def close(self, conn=None):
        if conn is None:
            conn = self.sock
        print('close', conn)
        self.sel.unregister(conn)
        self.onclose(conn)
        conn.close()

    def onconnectionmade(self, conn):
        pass

    def onconnectionfail(self, conn):
        pass

    def onread(self, conn, data):
        pass

    def onsend(self, conn):
        pass

    def onclose(self, conn):
        pass

    def waitread(self, conn):
        self.sel.modify(conn, selectors.EVENT_READ, self._read)

    def waitsend(self, conn):
        self.sel.modify(conn, selectors.EVENT_WRITE, self._send)

    def _checkanddosendjob(self, conn):
        if self._base_client_net_data_to_send:
            sended = conn.send(self._base_client_net_data_to_send)
            if sended < len(self._base_client_net_data_to_send):
                self._base_client_net_data_to_send = self._base_client_net_data_to_send[sended:]
            else:
                self._base_client_net_data_to_send = None
                if self._base_client_net_onsendend:
                    self._base_client_net_onsendend(conn)
                else:
                    self.waitread(conn)
            return True
        return False
